/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit;

import org.eclipse.draw2d.AnchorListener;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.NamedElement;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editpart.IUMLElementEditPart;
import com.cea.papyrus.core.ui.properties.advanced.EObjectPropertySource;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.Di2Package;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;

/**
 * Abstract edit part for graphical model elements.
 * 
 * @author frankd@ifi.uio.no
 */
public abstract class AbstractLinkEditPart<T extends NamedElement> extends
//GraphEdgeEditPart{ 
  AbstractConnectionEditPart implements IUMLElementEditPart<T> {

    private ModelManager modelManager;

    private IPropertySource propertySource;

    public AbstractLinkEditPart(GraphEdge view, ModelManager modelManager) {
    	if (view == null || ModelUtil.<NamedElement> getSemantic(view) == null)
            throw new IllegalArgumentException("null");
        setModel(view);
        this.modelManager = modelManager;
    }

    @SuppressWarnings("unchecked")
    protected GraphEdge getLinkModel() {
        return (GraphEdge) getModel();
    }

    @Override
    public Command getCommand(Request request) {
        return super.getCommand(request);
    }

    // --- Synchronization ---

    protected void graphicalModelChanged(Notification evt) {
        switch (evt.getFeatureID(Di2Package.class)) {
        case Di2Package.GRAPH_EDGE__ANCHOR:
        case Di2Package.GRAPH_EDGE__ANCHORAGE:
            refresh();
        }
        int type = evt.getEventType();
        if ((type == Notification.SET) || (type == Notification.ADD)) {
            if (((GraphElement) this.getModel()).getContainer() != null) {
                this.getModelManager().modifGraphElement((GraphEdge) this.getModel());
                this.refresh();
            }
        }
        //
        // test anchors :
    }

    /** Subclasses should override this method. */
    protected abstract void umlModelChanged(Notification msg);

    // --- Activation ---

    @SuppressWarnings("unchecked")
    @Override
    public void activate() {
        if (!isActive()) {
            super.activate();
            getLinkModel().eAdapters().add(graphicalAdapter);
            this.getLinkModel().getAnchor().get(0).eAdapters().add(graphicalAdapter);
            this.getLinkModel().getAnchor().get(1).eAdapters().add(graphicalAdapter);
            getUmlElement().eAdapters().add(umlAdapter);
            this.getSourceConnectionAnchor().addAnchorListener(sourceAnchorUpdater);
            this.getTargetConnectionAnchor().addAnchorListener(targetAnchorUpdater);
        }
    }

    private AnchorUpdater sourceAnchorUpdater = new AnchorUpdater(true);
    private AnchorUpdater targetAnchorUpdater = new AnchorUpdater(false);

    @Override
    public void deactivate() {
        if (isActive()) {
            getLinkModel().eAdapters().remove(graphicalAdapter);
            getLinkModel().getAnchor().get(0).eAdapters().remove(graphicalAdapter);
            getLinkModel().getAnchor().get(1).eAdapters().remove(graphicalAdapter);
            if (getUmlElement() != null)
                getUmlElement().eAdapters().remove(umlAdapter);
            this.getSourceConnectionAnchor().removeAnchorListener(sourceAnchorUpdater);
            this.getTargetConnectionAnchor().removeAnchorListener(targetAnchorUpdater);
            super.deactivate();
        }
    }

    private class AnchorUpdater implements AnchorListener {

        private boolean source;

        public AnchorUpdater(boolean source) {
            this.source = source;
        }

        /**
         * @see org.eclipse.draw2d.AnchorListener#anchorMoved(org.eclipse.draw2d.ConnectionAnchor)
         */
        public void anchorMoved(ConnectionAnchor anchor) {
            //
        }
    }

    protected final AdapterImpl umlAdapter = new AdapterImpl() {
        public boolean isAdapterForType(Object type) {
            return type instanceof NamedElement;
        }

        public void notifyChanged(Notification msg) {
            if (msg.isTouch() || (msg.getOldValue() == msg.getNewValue()))
                return;
            AbstractLinkEditPart.this.umlModelChanged(msg);
        }
    };

    protected final AdapterImpl graphicalAdapter = new AdapterImpl() {
        public boolean isAdapterForType(Object type) {
            return type instanceof GraphElement;
        }

        public void notifyChanged(Notification msg) {
            if (msg.isTouch() || (msg.getOldValue() == msg.getNewValue()))
                return;
            AbstractLinkEditPart.this.graphicalModelChanged(msg);
        }
    };

    /**
     * @see com.cea.papyrus.common.editpart.IUMLElementEditPart#getUmlElement()
     */
    public T getUmlElement() {
    	return ModelUtil.<T> getSemantic(getLinkModel());
    }

    /**
     * @see com.cea.papyrus.common.editpart.IUMLElementEditPart#getElementImport()
     */
    public ElementImport getElementImport() {
        return (ElementImport) this.getUmlElement();
    }

    /**
     * @see com.cea.papyrus.common.editpart.IUMLElementEditPart#isElementImport()
     */
    public boolean isElementImport() {
        return this.getUmlElement() instanceof ElementImport;
    }

    /**
     * @see com.cea.papyrus.common.editpart.IUMLElementEditPart#getModelManager()
     */
    public ModelManager getModelManager() {
        return this.modelManager;
    }

    /**
     * @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String,
     *      java.lang.String)
     */
    public boolean testAttribute(Object target, String name, String value) {
        if (target instanceof AbstractLinkEditPart) {
            Element targetUmlElement = ((AbstractLinkEditPart<?>) target).getUmlElement();
            return umlFilter.testAttribute(targetUmlElement, name, value);
        }
        return false;
    }

    /**
     * Gets the adapter.
     * 
     * @param key
     *            the key
     * @return the adapter
     */
    @Override
    public Object getAdapter(Class key) {
        if (IPropertySource.class == key) {
            return this.getPropertySource();
        }

        return super.getAdapter(key);
    }

    /**
     * Gets the property source.
     * 
     * @return the property source
     */
    protected IPropertySource getPropertySource() {
        if (this.propertySource == null) {
            this.propertySource = new EObjectPropertySource(this.getUmlElement());
        }
        return this.propertySource;
    } 
    
	/**
	 * Gets the graph edge.
	 * 
	 * @return the graph edge
	 */
	public GraphEdge getGraphEdge() {
		return (GraphEdge) getModel();
	}
}
